home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / non-ANSI / c-client / smtp.c < prev    next >
C/C++ Source or Header  |  1996-06-28  |  12KB  |  375 lines

  1. /*
  2.  * Program:    Simple Mail Transfer Protocol (SMTP) routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    27 July 1988
  13.  * Last Edited:    28 June 1996
  14.  *
  15.  * Sponsorship:    The original version of this work was developed in the
  16.  *        Symbolic Systems Resources Group of the Knowledge Systems
  17.  *        Laboratory at Stanford University in 1987-88, and was funded
  18.  *        by the Biomedical Research Technology Program of the National
  19.  *        Institutes of Health under grant number RR-00785.
  20.  *
  21.  * Original version Copyright 1988 by The Leland Stanford Junior University
  22.  * Copyright 1994 by the University of Washington
  23.  *
  24.  *  Permission to use, copy, modify, and distribute this software and its
  25.  * documentation for any purpose and without fee is hereby granted, provided
  26.  * that the above copyright notices appear in all copies and that both the
  27.  * above copyright notices and this permission notice appear in supporting
  28.  * documentation, and that the name of the University of Washington or The
  29.  * Leland Stanford Junior University not be used in advertising or publicity
  30.  * pertaining to distribution of the software without specific, written prior
  31.  * permission.  This software is made available "as is", and
  32.  * THE UNIVERSITY OF WASHINGTON AND THE LELAND STANFORD JUNIOR UNIVERSITY
  33.  * DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO THIS SOFTWARE,
  34.  * INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  35.  * FITNESS FOR A PARTICULAR PURPOSE, AND IN NO EVENT SHALL THE UNIVERSITY OF
  36.  * WASHINGTON OR THE LELAND STANFORD JUNIOR UNIVERSITY BE LIABLE FOR ANY
  37.  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
  38.  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
  39.  * CONTRACT, TORT (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF
  40.  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  41.  *
  42.  */
  43.  
  44. #include <ctype.h>
  45. #include <stdio.h>
  46. #include "mail.h"
  47. #include "osdep.h"
  48. #include "smtp.h"
  49. #include "rfc822.h"
  50. #include "misc.h"
  51.  
  52.  
  53. /* Mailer parameters */
  54.  
  55. long smtp_port = 0;        /* default port override */
  56.  
  57. /* Mail Transfer Protocol open connection
  58.  * Accepts: service host list
  59.  *        SMTP open options
  60.  * Returns: T on success, NIL on failure
  61.  */
  62.  
  63. SMTPSTREAM *smtp_open (hostlist,options)
  64.     char **hostlist;
  65.     long options;
  66. {
  67.   SMTPSTREAM *stream = NIL;
  68.   char *s,tmp[MAILTMPLEN];
  69.   void *tcpstream;
  70.   if (!(hostlist && *hostlist)) mm_log ("Missing SMTP service host",ERROR);
  71.   else do {            /* try to open connection */
  72.     if (smtp_port) sprintf (s = tmp,"%s:%ld",*hostlist,smtp_port);
  73.     else s = *hostlist;        /* get server name */
  74.     if (tcpstream = tcp_open (s,"smtp",SMTPTCPPORT)) {
  75.       stream = (SMTPSTREAM *) fs_get (sizeof (SMTPSTREAM));
  76.       stream->tcpstream = tcpstream;
  77.       if(smtp_greeting (stream,
  78.              strcmp ("localhost",(char*)lcase(strcpy(tmp,*hostlist)))
  79.                   ? tcp_localhost (tcpstream) : "localhost",
  80.              options))
  81.     return stream;
  82.       smtp_close (stream);    /* otherwise punt stream */
  83.     }
  84.   } while (*++hostlist);    /* try next server */
  85.   return NIL;
  86. }
  87.  
  88.  
  89. /* Mail Transfer Protocol salutation
  90.  * Accepts: local host name
  91.  *        SMTP open options
  92.  * Returns: T on success, NIL on failure
  93.  */
  94. long smtp_greeting (stream,lhost,options)
  95.     SMTPSTREAM *stream;
  96.     char *lhost;
  97.     long options;
  98. {
  99.   stream->size = 0;        /* size limit */
  100.   stream->debug = (options & SOP_DEBUG) ? T : NIL;
  101.   stream->esmtp = (options & SOP_ESMTP) ? T : NIL;
  102.   stream->ok_send = stream->ok_soml = stream->ok_saml = stream->ok_expn =
  103.     stream->ok_help = stream->ok_turn = stream->ok_size =
  104.       stream->ok_8bitmime = NIL;
  105.   stream->reply = NIL;
  106.                 /* get SMTP greeting */
  107.   if (smtp_reply (stream) == SMTPGREET) {
  108.     if ((stream->ehlo = stream->esmtp) &&
  109.     (smtp_send (stream,"EHLO",lhost) == SMTPOK)) return T;
  110.                 /* try ordinary SMTP then */
  111.     stream->ehlo = stream->esmtp = NIL;
  112.     if (smtp_send (stream,"HELO",lhost) == SMTPOK) return T;
  113.   }
  114.   mm_log (stream->reply,ERROR);
  115.   return NIL;
  116. }
  117. /* Mail Transfer Protocol close connection
  118.  * Accepts: stream
  119.  * Returns: NIL always
  120.  */
  121.  
  122. SMTPSTREAM *smtp_close (stream)
  123.     SMTPSTREAM *stream;
  124. {
  125.   if (stream) {            /* send "QUIT" */
  126.     smtp_send (stream,"QUIT",NIL);
  127.                 /* close TCP connection */
  128.     (* (postclose_t) mail_parameters (NIL,GET_POSTCLOSE,NIL)) (stream->tcpstream);
  129.     if (stream->reply) fs_give ((void **) &stream->reply);
  130.     fs_give ((void **) &stream);/* flush the stream */
  131.   }
  132.   return NIL;
  133. }
  134.  
  135. /* Mail Transfer Protocol deliver mail
  136.  * Accepts: stream
  137.  *        delivery option (MAIL, SEND, SAML, SOML)
  138.  *        message envelope
  139.  *        message body
  140.  * Returns: T on success, NIL on failure
  141.  */
  142.  
  143. long smtp_mail (stream,type,env,body)
  144.     SMTPSTREAM *stream;
  145.     char *type;
  146.     ENVELOPE *env;
  147.     BODY *body;
  148. {
  149.   char tmp[8*MAILTMPLEN];
  150.   long error = NIL;
  151.   rfc822emit_t f;
  152.   if (!(env->to || env->cc || env->bcc)) {
  153.                   /* no recipients in request */
  154.     smtp_fake (stream,SMTPHARDERROR,"No recipients specified");
  155.     return NIL;
  156.   }
  157.                   /* make sure stream is in good shape */
  158.   smtp_send (stream,"RSET",NIL);
  159.   strcpy (tmp,"FROM:<");    /* compose "MAIL FROM:<return-path>" */
  160.   rfc822_address (tmp,env->return_path);
  161.   strcat (tmp,">");
  162.   if (stream->ok_8bitmime) strcat (tmp," BODY=8BITMIME");
  163.                 /* send "MAIL FROM" command */
  164.   if (!(smtp_send (stream,type,tmp) == SMTPOK)) return NIL;
  165.                 /* negotiate the recipients */
  166.   if (env->to) smtp_rcpt (stream,env->to,&error);
  167.   if (env->cc) smtp_rcpt (stream,env->cc,&error);
  168.   if (env->bcc) smtp_rcpt (stream,env->bcc,&error);
  169.   if (error) {            /* any recipients failed? */
  170.                       /* reset the stream */
  171.     smtp_send (stream,"RSET",NIL);
  172.     smtp_fake (stream,SMTPHARDERROR,"One or more recipients failed");
  173.     return NIL;
  174.   }
  175.                 /* negotiate data command */
  176.   if (!(smtp_send (stream,"DATA",NIL) == SMTPREADY)) return NIL;
  177.                 /* set up error in case failure */
  178.   smtp_fake (stream,SMTPSOFTFATAL,"SMTP connection went away!");
  179.                 /* encode body as necessary */
  180.   if((f = mail_parameters (NIL,GET_RFC822OUTPUT,NIL)) == (rfc822emit_t) rfc822_output){
  181.                 /* encode body as necessary */
  182.       if (stream->ok_8bitmime) rfc822_encode_body_8bit (env,body);
  183.       else rfc822_encode_body_7bit (env,body);
  184.   }
  185.                 /* output data, return success status */
  186.   return (*f) (tmp,env,body,smtp_soutr,stream->tcpstream) &&
  187.     (smtp_send (stream,".",NIL) == SMTPOK);
  188. }
  189.  
  190. /* Mail Transfer Protocol turn on debugging telemetry
  191.  * Accepts: stream
  192.  */
  193.  
  194. void smtp_debug (stream)
  195.     SMTPSTREAM *stream;
  196. {
  197.   stream->debug = T;        /* turn on protocol telemetry */
  198. }
  199.  
  200.  
  201. /* Mail Transfer Protocol turn off debugging telemetry
  202.  * Accepts: stream
  203.  */
  204.  
  205. void smtp_nodebug (stream)
  206.     SMTPSTREAM *stream;
  207. {
  208.   stream->debug = NIL;        /* turn off protocol telemetry */
  209. }
  210.  
  211. /* Internal routines */
  212.  
  213.  
  214. /* Simple Mail Transfer Protocol send recipient
  215.  * Accepts: SMTP stream
  216.  *        address list
  217.  *        pointer to error flag
  218.  */
  219.  
  220. void smtp_rcpt (stream,adr,error)
  221.     SMTPSTREAM *stream;
  222.     ADDRESS *adr;
  223.     long *error;
  224. {
  225.   char tmp[MAILTMPLEN];
  226.   while (adr) {
  227.                 /* clear any former error */
  228.     if (adr->error) fs_give ((void **) &adr->error);
  229.     if (adr->host) {        /* ignore group syntax */
  230.       strcpy (tmp,"TO:<");    /* compose "RCPT TO:<return-path>" */
  231.       rfc822_address (tmp,adr);
  232.       strcat (tmp,">");
  233.                 /* send "RCPT TO" command */
  234.       if (!(smtp_send (stream,"RCPT",tmp) == SMTPOK)) {
  235.     *error = T;        /* note that an error occurred */
  236.     adr->error = cpystr (stream->reply);
  237.       }
  238.     }
  239.     adr = adr->next;        /* do any subsequent recipients */
  240.   }
  241. }
  242.  
  243.  
  244. /* Simple Mail Transfer Protocol send command
  245.  * Accepts: SMTP stream
  246.  *        text
  247.  * Returns: reply code
  248.  */
  249.  
  250. long smtp_send (stream,command,args)
  251.     SMTPSTREAM *stream;
  252.     char *command;
  253.     char *args;
  254. {
  255.   char tmp[MAILTMPLEN];
  256.                 /* build the complete command */
  257.   if (args) sprintf (tmp,"%s %s",command,args);
  258.   else strcpy (tmp,command);
  259.   if (stream->debug) mm_dlog (tmp);
  260.   strcat (tmp,"\015\012");
  261.                 /* send the command */
  262.   return (*(postsoutr_t) mail_parameters (NIL,GET_POSTSOUTR,NIL))
  263.                             (stream->tcpstream,tmp)
  264.        ? smtp_reply (stream)
  265.        : smtp_fake (stream,SMTPSOFTFATAL,"SMTP connection went away!");
  266. }
  267.  
  268. /* Simple Mail Transfer Protocol get reply
  269.  * Accepts: SMTP stream
  270.  * Returns: reply code
  271.  */
  272.  
  273. long smtp_reply (stream)
  274.     SMTPSTREAM *stream;
  275. {
  276.   unsigned long i,j;
  277.   postgetline_t getline =
  278.     (postgetline_t) mail_parameters (NIL,GET_POSTGETLINE,NIL);
  279.   postverbose_t pv;
  280.                 /* flush old reply */
  281.   if (stream->reply) fs_give ((void **) &stream->reply);
  282.                   /* get reply */
  283.   if (!(stream->reply = (*getline) (stream->tcpstream)))
  284.     return smtp_fake (stream,SMTPSOFTFATAL,"SMTP connection went away!");
  285.   if (stream->debug) mm_dlog (stream->reply);
  286.                 /* got an OK reply? */
  287.   if (((i = atoi (stream->reply)) == SMTPOK) && stream->ehlo) {
  288.     char tmp[MAILTMPLEN];    /* yes, make uppercase copy of response text */
  289.     ucase (strcpy (tmp,stream->reply+4));
  290.                 /* command name */
  291.     j = (((long) tmp[0]) << 24) + (((long) tmp[1]) << 16) +
  292.       (((long) tmp[2]) << 8) + tmp[3];
  293.                 /* defined by SMTP 8bit-MIMEtransport */
  294.     if (j == (((long) '8' << 24) + ((long) 'B' << 16) + ('I' << 8) + 'T') &&
  295.     tmp[4] == 'M' && tmp[5] == 'I' && tmp[6] == 'M' && tmp[7] == 'E' &&
  296.     !tmp[8]) stream->ok_8bitmime = T;
  297.                 /* defined by SMTP Size Declaration */
  298.     else if (j == (((long) 'S' << 24) + ((long) 'I' << 16) + ('Z' << 8) + 'E')
  299.          && (!tmp[4] || tmp[4] == ' ')) {
  300.       if (tmp[4]) stream->size = atoi (tmp+5);
  301.       stream->ok_size = T;
  302.     }
  303.                 /* defined by SMTP Service Extensions */
  304.     else if (j == (((long) 'S' << 24) + ((long) 'E' << 16) + ('N' << 8) + 'D')
  305.          && !tmp[4]) stream->ok_send = T;
  306.     else if (j == (((long) 'S' << 24) + ((long) 'O' << 16) + ('M' << 8) + 'L')
  307.          && !tmp[4]) stream->ok_soml = T;
  308.     else if (j == (((long) 'S' << 24) + ((long) 'A' << 16) + ('M' << 8) + 'L')
  309.          && !tmp[4]) stream->ok_saml = T;
  310.     else if (j == (((long) 'E' << 24) + ((long) 'X' << 16) + ('P' << 8) + 'N')
  311.          && !tmp[4]) stream->ok_expn = T;
  312.     else if (j == (((long) 'H' << 24) + ((long) 'E' << 16) + ('L' << 8) + 'P')
  313.          && !tmp[4]) stream->ok_help = T;
  314.     else if (j == (((long) 'T' << 24) + ((long) 'U' << 16) + ('R' << 8) + 'N')
  315.          && !tmp[4]) stream->ok_turn = T;
  316.   }
  317.   else if (i < 100 && (pv = mail_parameters (NIL,GET_POSTVERBOSE,NIL))){
  318.     (*pv) (stream->reply);
  319.     return smtp_reply(stream);
  320.   }
  321.                 /* handle continuation by recursion */
  322.   if (stream->reply[3]=='-') return smtp_reply (stream);
  323.   stream->ehlo = NIL;        /* not doing EHLO any more */
  324.   return i;            /* return the response code */
  325. }
  326.  
  327. /* Simple Mail Transfer Protocol set fake error
  328.  * Accepts: SMTP stream
  329.  *        SMTP error code
  330.  *        error text
  331.  * Returns: error code
  332.  */
  333.  
  334. long smtp_fake (stream,code,text)
  335.     SMTPSTREAM *stream;
  336.     long code;
  337.     char *text;
  338. {
  339.                 /* flush any old reply */
  340.   if (stream->reply ) fs_give ((void **) &stream->reply);
  341.                   /* set up pseudo-reply string */
  342.   stream->reply = (char *) fs_get (20+strlen (text));
  343.   sprintf (stream->reply,"%ld %s",code,text);
  344.   return code;            /* return error code */
  345. }
  346.  
  347.  
  348. /* Simple Mail Transfer Protocol filter mail
  349.  * Accepts: stream
  350.  *        string
  351.  * Returns: T on success, NIL on failure
  352.  */
  353.  
  354. long smtp_soutr (stream,s)
  355.     void *stream;
  356.     char *s;
  357. {
  358.   char c,*t;
  359.   postsoutr_t soutr =
  360.     (postsoutr_t) mail_parameters (NIL, GET_POSTSOUTR, 0);
  361.                 /* "." on first line */
  362.   if (s[0] == '.') (*soutr) (stream,".");
  363.                 /* find lines beginning with a "." */
  364.   while (t = strstr (s,"\015\012.")) {
  365.     c = *(t += 3);        /* remember next character after "." */
  366.     *t = '\0';            /* tie off string */
  367.                 /* output prefix */
  368.     if (!(*soutr) (stream,s)) return NIL;
  369.     *t = c;            /* restore delimiter */
  370.     s = t - 1;            /* push pointer up to the "." */
  371.   }
  372.                 /* output remainder of text */
  373.   return *s ? (*soutr) (stream,s) : T;
  374. }
  375.